/*
 * Copyright (C) 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.apps.santatracker.presentquest.ui.components;

import android.content.Context;
import android.os.Handler;

import androidx.core.content.ContextCompat;

import com.google.android.apps.santatracker.presentquest.R;
import com.google.android.gms.maps.model.Circle;

/** Animate a {@link Circle} that pulses and fades. */
public class CirclePulseAnimator {

    private static final int STEP_TIME_MS = 32;

    private static final double MIN_ACTION_RADIUS = 10.0f;
    private static final double ACTION_RADIUS_STEP = 1.2f;
    private final double MAX_ACTION_RADIUS;
    private static final int ACTION_OPACITY_STEP = 2;

    private Context mContext;
    private Circle mCircle;

    public CirclePulseAnimator(Context context, Circle circle, int reachableRadius) {
        mContext = context;
        mCircle = circle;
        // Multiply by 1.2 so that it appears to "reach" all reachable icons
        MAX_ACTION_RADIUS = reachableRadius * 1.2f;
    }

    private Handler mHorizonAnimationHandler = new Handler();
    private Runnable mPulseHorizonsRunnable =
            new Runnable() {
                @Override
                public void run() {
                    // Calculate new radius
                    double newRadius = mCircle.getRadius() + ACTION_RADIUS_STEP;

                    // Calculate new colors (change opacity)
                    int strokeColor = mCircle.getStrokeColor();
                    int newStrokeOpacity =
                            Math.max(getOpacity(strokeColor) - ACTION_OPACITY_STEP, 0);
                    int newStrokeColor = setOpacity(strokeColor, newStrokeOpacity);

                    int fillColor = mCircle.getFillColor();
                    int newFillOpacity = Math.max(getOpacity(fillColor) - ACTION_OPACITY_STEP, 0);
                    int newFillColor = setOpacity(fillColor, newFillOpacity);

                    if (newRadius <= MAX_ACTION_RADIUS) {
                        // Set to new values
                        mCircle.setRadius(newRadius);
                        mCircle.setStrokeColor(newStrokeColor);
                        mCircle.setFillColor(newFillColor);
                    } else {
                        // Reset
                        mCircle.setRadius(MIN_ACTION_RADIUS);
                        mCircle.setStrokeColor(
                                ContextCompat.getColor(mContext, R.color.action_horizon_stroke));
                        mCircle.setFillColor(
                                ContextCompat.getColor(mContext, R.color.action_horizon_fill));
                    }

                    // Reschedule
                    mHorizonAnimationHandler.postDelayed(this, STEP_TIME_MS);
                }
            };

    public void start() {
        mHorizonAnimationHandler.removeCallbacksAndMessages(null);
        mHorizonAnimationHandler.post(mPulseHorizonsRunnable);
    }

    public void stop() {
        mHorizonAnimationHandler.removeCallbacksAndMessages(null);
    }

    /**
     * Change the opacity of a hexadecimal color.
     *
     * @param color the color.
     * @param opacity a new opacity value, 0 to 255.
     * @return the new color.
     */
    private int setOpacity(int color, int opacity) {
        // Remove opacity
        int currentOpacity = ((color >> 24) & 0xFF) << 24;
        color = (color - currentOpacity);

        // Add new opacity
        color = color + (opacity << 24);

        return color;
    }

    /**
     * Get the current opacity of a hexadecimal color.
     *
     * @param color the color.
     * @return the opacity, 0 to 255.
     */
    private int getOpacity(int color) {
        return (color >> 24) & 0xFF;
    }
}
